package tdengine

import (
	"fmt"
	"regexp"
	"strings"
	"sync"
	"time"
)

var (
	cofg Config
	once sync.Once
)

// Config include all scene import config
type Config struct {
	UserCases map[string]CaseConfig
}

const (
	// 主键类型必须为 timestamp
	TIMESTAMP = "timestamp"

	// 样例数据中主键时间字段是 millisecond 还是 dateTime 格式
	DATETIME    = "datetime"
	MILLISECOND = "millisecond"

	DefaultStartTime int64 = -1
	DefaultInterval  int64 = 1 * 1000 // 导入的记录时间间隔，该设置只会在指定 auto=1 之后生效，否则会根据样例数据自动计算间隔时间。单位为毫秒，默认 1000。
	DefaultDelay     int64 = -1       //

	// 当 save 为 1 时保存统计信息的表名， 默认 statistic。
	DefaultStatisticTable = "statistic"

	// 样例数据文件格式，可以是 json 或 csv
	JsonFormat = "json"
	CsvFormat  = "csv"

	SuperTablePrefix = "s_" // 超级表前缀
	SubTablePrefix   = "t_" // 子表前缀

	DriverName      = "taosSql"
	StartTimeLayout = "2006-01-02 15:04:05.000"
	InsertPrefix    = "insert into "
)

// CaseConfig include the sample data config and tdengine config
type CaseConfig struct {
	Format              string
	FilePath            string
	Separator           string
	StName              string
	SubTableName        string
	Timestamp           string
	TimestampType       string
	TimestampTypeFormat string
	Tags                []FieldInfo
	Fields              []FieldInfo
}

// FieldInfo is field or tag info
type FieldInfo struct {
	Name string
	Type string
}

// Loading option vars:
type LoadingOptions struct {
	cfg            string // 导入配置文件路径，包含样例数据文件相关描述及对应 TDengine 配置信息。默认使用 config/cfg.toml
	Cases          string // 需要导入的场景名称，该名称可从 -cfg 指定的配置文件中 [usecase] 查看，可同时导入多个场景，中间使用逗号分隔，如：sensor_info,camera_detection，默认为 sensor_info
	Hnum           int    // 需要将样例数据进行横向扩展的倍数，假设原有样例数据包含 1 张子表 t_0 数据，指定 hnum 为 2 时会根据原有表名创建 t、t_1 两张子表。默认为 100。
	Vnum           int    // 需要将样例数据进行纵向扩展的次数，如果设置为 0 代表将历史数据导入至当前时间后持续按照指定间隔导入。默认为 1000，表示将样例数据在时间轴上纵向复制1000 次
	Thread         int    // 执行导入数据的线程数目，默认为 10
	Batch          int    // 执行导入数据时的批量大小，默认为 100。批量是指一次写操作时，包含多少条记录
	Auto           int    // 是否自动生成样例数据中的主键时间戳，1 是，0 否， 默认 0
	StartTimeStr   string // 导入的记录开始时间，格式为 "yyyy-MM-dd HH:mm:ss.SSS"，不设置会使用样例数据中最小时间，设置后会忽略样例数据中的主键时间，会按照指定的 start 进行导入。如果 auto 为 1，则必须设置 start，默认为空
	Interval       int64  // 导入的记录时间间隔，该设置只会在指定 auto=1 之后生效，否则会根据样例数据自动计算间隔时间。单位为毫秒，默认 1000
	Host           string // 导入的 TDengine 服务器 IP，默认为 127.0.0.1
	Port           int    // 导入的 TDengine 服务器端口，默认为 6030
	User           string // 导入的 TDengine 用户名，默认为 root
	Password       string // 导入的 TDengine 用户密码，默认为 taosdata
	Dropdb         int    // 导入数据之前是否删除数据库，1 是，0 否， 默认 0
	Db             string // 导入的 TDengine 数据库名称，默认为 test_yyyyMMdd
	Dbparam        string // 当指定的数据库不存在时，自动创建数据库时可选项配置参数，如 days 10 cache 16000 ablocks 4，默认为空
	ConnDB         string `yaml:"admin-db-name" mapstructure:"admin-db-name"`
	DataSourceName string
	StartTime      int64

	SuperTableConfigMap map[string]*superTableConfig
	SubTableMap         map[string]*dataRows
	ScaleTableNames     []string

	ScaleTableMap map[string]*scaleTableInfo

	SuccessRows    []int64
	LastStaticTime time.Time
	LastTotalRows  int64
	TimeTicker     *time.Ticker
	Delay          int64  // 当 vnum 设置为 0 时持续导入的时间间隔，默认为所有场景中最小记录间隔时间的一半，单位 ms。
	Tick           int64  // 打印统计信息的时间间隔，默认 2000 ms。
	Save           int    // 是否保存统计信息到 tdengine 的 statistic 表中，1 是，0 否， 默认 0。
	SaveTable      string // 当 save 为 1 时保存统计信息的表名， 默认 statistic。
	TagColumnTypes []string
}
type superTableConfig struct {
	startTime   int64
	endTime     int64
	cycleTime   int64
	avgInterval int64
	config      CaseConfig
}

type scaleTableInfo struct {
	scaleTableName string
	subTableName   string
	insertRows     int64
}

//type tableRows struct {
//	tableName string // tableName
//	value     string // values(...)
//}

type dataRows struct {
	rows   []map[string]interface{}
	config CaseConfig
}

func (o *LoadingOptions) GetConnectString(filePath string) string {
	re := regexp.MustCompile(`(host|dbname|user)=\S*\b`)
	connectString := strings.TrimSpace(re.ReplaceAllString(o.cfg, ""))
	//o.User = "root"
	connectString = fmt.Sprintf("host=%s dbname=%s user=%s %s", o.Host, filePath, o.User, connectString)

	// For optional parameters, ensure they exist then interpolate them into the connectString
	if o.Port > 0 {
		connectString = fmt.Sprintf("%s port=%d", connectString, o.Port)
	}
	if len(o.Password) > 0 {
		connectString = fmt.Sprintf("%s password=%s", connectString, o.Password)
	}

	// we assume we're using pq driver
	connectString = fmt.Sprintf("%s:%s/tcp(%s:%d)/", o.User, o.Password, o.Host, o.Port)

	return connectString
}
